// Cook-Torrance shading model fragment shader
// Have to be used with FragmentShaderFunctions.frag

uniform vec4 fSurfaceColor;
uniform float fIsotropicRoughness;
uniform float fFresnelBase;
uniform vec2 fAnisotropicRoughness;

varying vec2 Texcoord;
varying vec3 ViewDirection;
varying vec3 LightDirection;
varying float Distance;
varying vec3 Tangent;

float fGetIsotropicMicrofacetDistributionFunction(vec3 normal, vec3 half)
{
   float fNDotH = max ( 0.0, dot( normal, half ) );
   float fNDotH2 = fNDotH * fNDotH;
   float fNDotH4 = fNDotH * fNDotH * fNDotH * fNDotH;
   float fIsotropicRoughness2 = fIsotropicRoughness * fIsotropicRoughness;
   
   float a = 4.0 * fIsotropicRoughness2 * fNDotH4;
   float b = (fNDotH2 - 1.0) / (fIsotropicRoughness2 * fNDotH2);
   
   return ((1.0 / a) * exp(b));
}

float fGetAnisotropicMicrofacetDistributionFunction(vec3 normal, vec3 tangent, vec3 half)
{
   float fNDotH = max ( 0.0, dot( normal, half ) );
   float fNDotH2 = fNDotH * fNDotH;
   float fNDotH4 = fNDotH * fNDotH * fNDotH * fNDotH;
   
   vec3 fvPlanar = normalize( half - fNDotH * normal );
   float fTDotP = max ( 0.0, dot( tangent, fvPlanar ) );
   float fTDotP2 = fTDotP * fTDotP;
   float fAnisotropicRoughness2 = fAnisotropicRoughness.x * fAnisotropicRoughness.y;
   
   float a = 4.0 * fAnisotropicRoughness2 * fNDotH4;
   float b1 = fTDotP2 / fAnisotropicRoughness.x;
   float b2 = (1.0 - fTDotP2) / fAnisotropicRoughness.y;
   float b3 = (fNDotH2 - 1.0) / fNDotH2;
   float b = (b1 + b2) * b3;
   
   return ((1.0 / a) * exp(b));
}

vec4 fvGetCookTorranceIsotropicIllumination(vec2 texCoord)
{
   vec3 fvLight = normalize( LightDirection );
   vec3 fvView = normalize( ViewDirection );
   vec3 fvHalf = normalize( fvLight + fvView );
   
   vec3 fvNormal = FS_GET_NORMAL(texCoord);
   
   float fNDotL = max ( 0.0, dot( fvNormal, fvLight ) );
   float fNDotV = max ( 0.0, dot( fvNormal, fvView ) );
   
   float a = fGetIsotropicMicrofacetDistributionFunction( fvNormal, fvHalf ) *
             fGetGeometricalAttenuationFactor( fvNormal, fvHalf, fvView, fvLight );
             
   float b = PI * fNDotV * fNDotL;
   float c = fGetFresnelFactor( fFresnelBase, fvHalf, fvView );
                               
   float fAttenuation = 1.0 / (gl_LightSource[0].constantAttenuation +
                               gl_LightSource[0].linearAttenuation * Distance +
                               gl_LightSource[0].quadraticAttenuation * Distance * Distance);
   
   return (gl_LightSource[0].specular * (c * a / b)) * fAttenuation;
}

vec4 fvGetCookTorranceAnisotropicIllumination(vec2 texCoord)
{
   vec3 fvLight = normalize( LightDirection );
   vec3 fvView = normalize( ViewDirection );
   vec3 fvHalf = normalize( fvLight + fvView );
   vec3 fvTangent = normalize( Tangent );
   
   vec3 fvNormal = FS_GET_NORMAL(texCoord);
   
   float fNDotL = max ( 0.0, dot( fvNormal, fvLight ) );
   float fNDotV = max ( 0.0, dot( fvNormal, fvView ) );
   
   float a = fGetAnisotropicMicrofacetDistributionFunction( fvNormal, fvTangent, fvHalf ) *
             fGetGeometricalAttenuationFactor( fvNormal, fvHalf, fvView, fvLight );
             
   float b = PI * fNDotV * fNDotL;
   float c = fGetFresnelFactor( fFresnelBase, fvHalf, fvView );
                               
   float fAttenuation = 1.0 / (gl_LightSource[0].constantAttenuation +
                               gl_LightSource[0].linearAttenuation * Distance +
                               gl_LightSource[0].quadraticAttenuation * Distance * Distance);
   
   return (gl_LightSource[0].specular * (c * a / b)) * fAttenuation;
}
